package com.dongrj.framework.web;

import com.dongrj.framework.core.exception.Assert;
import com.dongrj.framework.core.exception.SystemException;
import com.dongrj.framework.core.model.*;
import com.dongrj.framework.core.utils.CollectionUtils;
import com.dongrj.framework.core.utils.StringUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author dongrongjun
 * @date 2020/08/04
 */
@Slf4j
public abstract class BaseService<Model extends BaseModel<T>, ModelExample extends BaseExample, T> {

    private final Class<Model> modelClass;

    private final Function<T, Model> idFunction = (id) -> getMapper().selectByPrimaryKey(id, null);

    public BaseService() {
        modelClass = (Class<Model>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public int save(Model model) {
        Date date = new Date();
        setModelDefaultDate(model, date);
        return getMapper().insertSelective(model);
    }


    public int saveOrUpdate(Model model) {
        if (Objects.nonNull(model.getId())) {
            return updateByPrimaryKeySelective(model);
        }
        return save(model);
    }

    public int insertBatchSelective(List<Model> modelList) {
        return insertBatchSelective(modelList, CollectionUtils.size(modelList));
    }


    public int insertBatchSelective(List<Model> modelList, Integer maxSize) {
        if (CollectionUtils.isEmpty(modelList)) {
            return 0;
        }
        if (CollectionUtils.size(modelList) > maxSize) {
            Function<List<Model>, Integer> batch = (models) -> getMapper().insertBatchSelective(models);
            List<Optional<Integer>> rs = CollectionUtils.batch(modelList, batch, maxSize);
            List<Integer> collect = rs.stream().map(item -> item.orElse(0)).collect(Collectors.toList());
            return CollectionUtils.sumInteger(collect);
        }
        return getMapper().insertBatchSelective(modelList);
    }

    public Optional<Model> selectByIdOpt(T id) {
        Model model = idFunction.apply(id);
        if (Objects.isNull(model)) {
            log.info("selectByIdOpt.id={},modelClass={}", id, modelClass);
            return Optional.empty();
        }
        return Optional.of(model);
    }

    public Model selectById(T id) {
        return selectByIdOpt(id).orElseThrow(() -> new SystemException("没有查询内容"));
    }

    public Model selectById(T id, String... columns) {
        if (Objects.nonNull(columns)) {
            List<String> cols = Arrays.stream(columns).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
            return getMapper().selectByPrimaryKey(id, cols);
        }
        return selectById(id);
    }


    /**
     * 根据Id删除
     *
     * @param id ID
     */
    public int deleteById(T id) {
        Model model = selectById(id);
        return deleteModel(model);
    }


    /**
     * 删除根据Example
     */
    public int deleteByExample(ModelExample example) {
        return getMapper().deleteByExample(example);
    }


    public int deleteByIds(List<T> ids) {
        Assert.isTrue(CollectionUtils.size(ids) > 0, "主键集合不能为空!");
        List<Model> models = selectByIds(ids, null);
        int ret = 0;
        for (Model model : models) {
            ret = ret + deleteModel(model);
        }
        return ret;
    }


    /**
     * 设置默认分页参数
     *
     * @param example Example
     */
    private static <Example extends BaseExample> void checkCurrentPageAndPageSize(Example example) {
        int pageSize = example.getPageSize();
        int currentPage = example.getCurrentPage();

        Assert.check(pageSize > 0, "每页数量需要大于0条.");
        Assert.check(currentPage > 0, "页码数需要大于0.");

        if (pageSize > 100) {
            log.warn("每页数量{},{}", pageSize, example.getClass());
        }
        if (pageSize > 50) {
            log.warn("页码数{},{}", currentPage, example.getClass());
        }
        Assert.check(pageSize < 500, "每页数量不能大于500条.");
        // 计算分页起始位置
        example.configStart();
    }

    /**
     * 空分页结果
     *
     * @param example   查询Example
     * @param <Model>   查询MapperA
     * @param <Example> 查询实体
     * @return
     */
    private static <Model, Example extends BaseExample> Page<Model> emptyPage(Example example) {
        log.warn("当前的Count=0,正在返回空的Page对象!");
        Page<Model> page = new Page<>(example.getPageSize(), example.getCurrentPage());
        page.setData(Lists.newArrayList());
        page.setTotal(0L);
        return page;
    }


    /**
     * 设置Model默认时间
     *
     * @param model 实体
     * @param date  时间
     */
    private static <PK, Model extends BaseModel<PK>> void setModelDefaultDate(Model model, Date date) {
        if (Objects.isNull(model.getCreateTime())) {
            model.setCreateTime(date);
        }
        if (Objects.isNull(model.getUpdateTime())) {
            model.setUpdateTime(date);
        }
    }


    /**
     * 分页查询
     *
     * @param baseExample 查询Example
     * @param baseMapper  查询Mapper
     * @param <Model>     返回实体
     * @param <Example>   查询实体
     */
    private static <PK, Model extends BaseModel<PK>, Example extends BaseExample> Page<Model> selectByPage(Example baseExample, BaseMapper<Model, Example, PK> baseMapper) {
        checkCurrentPageAndPageSize(baseExample);
        BiFunction<Example, BaseMapper<Model, Example, PK>, Page<Model>> biFunction = (example, mapper) -> {
            long count = mapper.countByExampleSelective(example);
            if (count == 0L) {
                return emptyPage(example);
            }
            String orderByClause = example.getOrderByClause();
            if (StringUtils.isEmpty(orderByClause)) {
                example.setOrderByClause(ExampleUtils.descUpdateDate());
            }
            List<Model> models = mapper.selectByExampleSelective(example);
            return new Page<>(example.getPageSize(), example.getCurrentPage(), models, count);
        };
        return biFunction.apply(baseExample, baseMapper);
    }


    /**
     * 分页查询
     *
     * @param example 查询条件
     */
    public Page<Model> selectByPage(ModelExample example) {
        return selectByPage(example, getMapper());
    }


    /**
     * 根据Example查询
     *
     * @param example 查询条件
     */
    public List<Model> selectByExample(ModelExample example) {
        return selectByExample(example, getMapper());
    }


    /**
     * 根据Example查询(一条)
     *
     * @param example 查询条件
     */
    public Optional<Model> selectOneByExample(ModelExample example) {
        example.setCurrentPage(1);
        example.setPageSize(1);
        List<Model> models = selectByExample(example, getMapper());
        return CollectionUtils.first(models);
    }

    /**
     * @param baseExample 查询Example
     * @param baseMapper  查询Mapper
     * @param <Model>     返回实体
     * @param <Example>   查询实体
     */

    public static <PK, Model extends BaseModel<PK>, Example extends BaseExample> List<Model> selectByExample(Example baseExample, BaseMapper<Model, Example, PK> baseMapper) {
        baseExample.setCurrentPage(0);
        baseExample.setPageSize(0);
        return baseMapper.selectByExampleSelective(baseExample);
    }


    public long countByExampleSelective(ModelExample example) {
        return getMapper().countByExampleSelective(example);
    }


    public int updateByPrimaryKeySelective(Model model) {
        return updateByPrimaryKeySelective(true, model);
    }


    public int updateByPrimaryKeySelective(boolean isEditDate, Model model) {
        return updateByPrimaryKeySelective(isEditDate, model, null);
    }

    public int updateByPrimaryKeySelectiveWithColumns(Model model, ModelExample example) {
        return updateByPrimaryKeySelective(true, model, example);
    }


    public int updateByPrimaryKeySelective(boolean isEditDate, Model model, ModelExample example) {
        Assert.check(model.getId() != null, "主键ID不能为空!");
        setModelUpdateTime(isEditDate, model);
        boolean exampleEmpty = (example == null || CollectionUtils.isEmpty(example.getColumnList()));
        int result;
        if (exampleEmpty) {
            result = getMapper().updateByPrimaryKeySelective(model);
        } else {
            if (isEditDate) {
                List<String> columnList = example.getColumnList();
                if (!columnList.contains(ModelConst.EDIT_DATE)) {
                    columnList.add(ModelConst.EDIT_DATE);
                }
            }
            result = getMapper().updateByPrimaryKeyColumnsSelective(model, example);
        }
        return result;
    }

    /**
     * 设置实体的修改时间
     *
     * @param isEditDate 是否修改修改时间
     * @param model      实体
     * @param <PK>       主键
     * @param <Model>    实体
     */
    private static <PK, Model extends BaseModel<PK>> void setModelUpdateTime(boolean isEditDate, Model model) {
        if (isEditDate) {
            if (Objects.isNull(model.getUpdateTime())) {
                model.setUpdateTime(new Date());
            }
        } else {
            model.setUpdateTime(null);
        }
    }


    public int updateByExampleSelective(Model model, ModelExample example) {
        return updateByExampleSelective(true, model, example);
    }

    public int updateByExampleSelective(Boolean isEditDate, Model model, ModelExample example) {
        return updateByExampleSelective(isEditDate, model, example, getMapper());
    }


    public static <PK, Model extends BaseModel<PK>, Example extends BaseExample> int updateByExampleSelective(Boolean isEditDate, Model model, Example example, BaseMapper<Model, Example, PK> baseMapper) {
        Assert.check(Objects.isNull(model.getId()), "主键ID必须为空!");
        if (isEditDate) {
            if (Objects.isNull(model.getUpdateTime())) {
                model.setUpdateTime(new Date());
            }
        } else {
            model.setUpdateTime(null);
        }
        return baseMapper.updateByExampleSelective(model, example);
    }


    public List<Model> selectByIds(T... ids) {
        if (CollectionUtils.isNotEmpty(ids)) {
            return selectByIds(Lists.newArrayList(ids), null);
        }
        return Lists.newArrayListWithCapacity(0);
    }

    public List<Model> selectByIds(ModelExample example, T... ids) {
        if (CollectionUtils.isNotEmpty(ids)) {
            return selectByIds(Lists.newArrayList(ids), example);
        }
        return Lists.newArrayListWithCapacity(0);
    }

    public List<Model> selectByIds(List<T> ids) {
        return selectByIds(ids, null);
    }

    public List<Model> selectByIds(List<T> ids, ModelExample example) {
        Assert.isTrue(CollectionUtils.size(ids) > 0, "主键集合不能为空!");
        if (example == null) {
            return getMapper().selectByBatchIdPrimaryKey(ids, null);
        }
        return getMapper().selectByBatchIdPrimaryKey(ids, example.getColumns());
    }

    /**
     * 获取modelType名字
     */
    public String getModelClassName() {
        if (modelClass == null) {
            return null;
        }
        return modelClass.getName();
    }

    private int deleteModel(Model model) {
        return getMapper().deleteByPrimaryKey(model.getId());
    }


    public abstract ModelExample getExample();

    public abstract BaseMapper<Model, ModelExample, T> getMapper();


    public boolean enableCache() {
        return false;
    }


    public Model selectByUniqueKey(Model model) {
        T id = model.getId();
        if (Objects.nonNull(id)) {
            return selectById(model.getId());
        }
        return null;
    }
}
